package com.auth.manager.security.login;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.auth.manager.common.resp.Result;
import com.auth.manager.common.resp.ResultEnum;
import com.auth.manager.utils.JwtTokenUtil;
import com.auth.manager.utils.ResponseUtil;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @version 1.0.0
 * @Date: 2022/4/27 10:36
 * @Author ZhuYouBin
 * @Description 自定义用户登录逻辑
 */
@Component
public class LoginAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    public LoginAuthenticationFilter(LoginManager loginManager) {
        // 认证管理器
        setAuthenticationManager(loginManager);
        // 设置拦截路径
        setFilterProcessesUrl("/login");
    }

    // 用户认证方法
    // 我们重写这个方法,然后实现自定义的认证逻辑
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        /**
         * 这个方法里面要做什么逻辑处理呢？？？
         * 1、判断请求类型是否json类型
         * 2、判断请求方式POST
         * 3、获取请求参数: username,password
         * 4、封装 UsernamePasswordAuthenticationToken 对象
         * 5、调用 AuthenticationManager 认证管理器
         */
        // 1、获取请求类型
        String contentType = request.getContentType();
        if (!"application/json".equals(contentType)) {
            throw new AuthenticationServiceException("请求数据类型不是json格式");
        }

        // 2、判断请求方式POST
        String method = request.getMethod();
        if (!"POST".equals(method)) {
            throw new AuthenticationServiceException("请求方式不正确，必须是POST方式");
        }

        // 3、获取请求参数
        String username = "";
        String password = "";
        try {
            String requestBody = ResponseUtil.getBody(request);
            JSONObject json = JSON.parseObject(requestBody);
            username = json.getString("username");
            password = json.getString("password");
        } catch (IOException e) {
            throw new AuthenticationServiceException("登录请求参数不正确");
        }

        // 4、构造 Authentication 对象
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, password);
        // 5、调用 AuthenticationManager 管理器
        return this.getAuthenticationManager().authenticate(authentication);
    }

    // 用户认证成功处理器
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
        // 设置响应状态码,这里都可以自定义,我就设置200
        response.setStatus(HttpServletResponse.SC_OK);

        // TODO 认证成功,则生成 Jwt Token, 并且保存到 Redis 缓存里面
        String token = JwtTokenUtil.generateToken(authentication.getPrincipal().toString());

        // 设置响应数据
        Result result = new Result();
        result.setCode(ResultEnum.SUCCESS.getCode());
        result.setMessage("登录成功");
        result.setData(token);

        ResponseUtil.response(response, result);
    }

    // 用户认证失败处理器
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        // 设置响应状态码,我喜欢设置401,表示未认证(认证失败)
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        ResponseUtil.response(response, Result.failed(failed.getMessage()));
    }
}
